package com.wys.spring;

import com.wys.api.common.BaseErrorCode;
import com.wys.api.common.CommonCode;
import com.wys.api.common.R;
import com.wys.api.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.NestedRuntimeException;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.security.SignatureException;
import java.util.List;

/**
 * @author: ghx
 * @date 2021/8/16
 * @describe: 全局异常捕获
 */

@Order(-99)
@RestControllerAdvice
@Slf4j
public class WYSGlobalExceptionHandler {

    /**
     * 处理自定义的业务异常
     */
    @ExceptionHandler(value = BizException.class)
    public R bizExceptionHandler(HttpServletRequest request, BizException e) {
        log.error("业务异常，" + buildMessage(request) + "：" + e.getMessage());
        return R.fail(e.getStatus(), e.getMessage());
    }

    /**
     * 用户账号密码异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(value = BadCredentialsException.class)
    public R badCredentialsHandle(HttpServletRequest request, BadCredentialsException e) {
        log.error("账号密码异常，{}：{}", buildMessage(request), e.getMessage());
        return R.fail(CommonCode.UNAUTHORIZED, e.getMessage());
    }

    /**
     * 账号登录异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(value = AuthenticationException.class)
    public R authenticationExceptionHandle(HttpServletRequest request, AuthenticationException e) {
        log.error("登录异常，" + buildMessage(request) + "：" + e.getMessage());
        return R.fail(CommonCode.UNAUTHORIZED, e.getMessage());
    }

    /**
     * 权限校验异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(value = AccessDeniedException.class)
    public R accessDeniedException(HttpServletRequest request, AccessDeniedException e) {
        log.error("权限校验异常，{}：{}", buildMessage(request), e.getMessage());
        return R.fail(BaseErrorCode.FORBIDDEN.getStatus(), e.getMessage());
    }

    /**
     * 令牌校验异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(value = SignatureException.class)
    public R signatureException(HttpServletRequest request, SignatureException e) {
        log.error("令牌校验异常，{}：{}", buildMessage(request), e.getMessage());
        return R.fail(BaseErrorCode.FORBIDDEN.getStatus(), "非法令牌访问");
    }

//    /**
//     * token 获取异常
//     * TokenException
//     */
//    @ExceptionHandler(value = TokenException.class)
//    public R bizExceptionHandler(HttpServletRequest request, TokenException e) {
//        log.error("业务异常，" + buildMessage(request) + "：" + e.getMessage());
//        return R.fail(e.getCode(), e.getMessage());
//    }

    /**
     * 构建异常信息
     */
    private String buildMessage(HttpServletRequest request) {
        return "请求信息 [" + request.getMethod() + " " + request.getRequestURI() + "]";
    }

    /**
     * 对方法参数校验异常处理方法(仅对于表单提交有效，对于以json格式提交将会失效)
     * 如果是表单类型的提交，则spring会采用表单数据的处理类进行处理（进行参数校验错误时会抛出BindException异常）
     */
    @ExceptionHandler(BindException.class)
    public R handlerBindException(HttpServletRequest request, BindException exception) {
        return handlerNotValidException(request, exception);
    }


    @ExceptionHandler({IllegalArgumentException.class})
    public R handlerBindException(HttpServletRequest request, IllegalArgumentException exception) {
        return handlerNotValidException(request, exception);
    }


    /**
     * 对方法参数校验异常处理方法(前端提交的方式为json格式出现异常时会被该异常类处理)
     * json格式提交时，spring会采用json数据的数据转换器进行处理（进行参数校验时错误是抛出MethodArgumentNotValidException异常）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handlerArgumentNotValidException(
            HttpServletRequest request, MethodArgumentNotValidException exception) {
        return handlerNotValidException(request, exception);
    }

    public R
    handlerNotValidException(HttpServletRequest request, Exception e) {
        BindingResult result;
        if (e instanceof BindException) {
            BindException exception = (BindException) e;
            result = exception.getBindingResult();
        } else if (e instanceof RuntimeException) {
            RuntimeException runtimeException = (RuntimeException) e;
            return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), runtimeException.getMessage());
        } else {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            result = exception.getBindingResult();
        }

        String errorMsg = "";
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            if (!CollectionUtils.isEmpty(fieldErrors)) {
                errorMsg = fieldErrors.get(0).getDefaultMessage();
            }
        }
        log.info("参数异常，{} {}", buildMessage(request), errorMsg);
        return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), errorMsg);
    }

    /**
     * 其他全局异常在此捕获
     */
    @ExceptionHandler(Exception.class)
    public R handleException(HttpServletRequest request, Exception e) {
        log.error(buildMessage(request), e);
        if (e instanceof BizException) {
            BizException bizException = (BizException) e;
            return R.fail(bizException.getStatus(), bizException.getMessage());
        }
        if (e instanceof NestedRuntimeException) {
            NestedRuntimeException nestedRuntimeException = (NestedRuntimeException) e;
            return R.fail(BaseErrorCode.INTERNAL_ERROR.getStatus(), nestedRuntimeException.getMessage());
        }

        return R.fail(BaseErrorCode.INTERNAL_ERROR.getMessage());
    }
}


